package com.eobard.obfuscate.optional;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.eobard.config.obfuscate.ObfuscatorConfig;
import com.eobard.enums.RunningEnum;
import com.eobard.obfuscate.IObfuscator;
import com.eobard.params.PackageRemoveParams;
import com.eobard.utils.*;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.PackageDeclaration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @Author: Eobard Gu
 * @Date: 2025-08-05
 * @Description: 包移动核心混淆类, 暂不支持移动源包到已有的包中
 */
@Component
@Slf4j
public class PackageRemoveObfuscator implements IObfuscator {
    private static String sourceRoot;

    private static boolean delOldFiles;
    //所有java文件map
    private static Map<String, CompilationUnit> fileMap = new HashMap<>();

    //是否为安卓项目,设为true则会修改对应的xml资源
    private static boolean isAndroidProject;
    //资源文件的地址
    private static String resourceRoot;

    //所有xml资源文件的map
    private static Map<String, File> allResourcesMap = null;

    //=================================源包配置=========================================//
    //输入需要转变重命名的源包名
    private static List<String> sourcePackages = null;

    //输入“指定包名”查找对应fileMap下的K名
    private static List<String> oldKey = null;

    //oldKey中获取CompilationUnit中的包名路径
    private static List<String> oldPkgUrl = null;

    //=================================目标包配置========================================//
    //输入转换后的目标包名
    private static List<String> targetPackages = null;

    //输入“想要转换后的目标包名”查找对应fileMap下的K名
    private static List<String> newKey = null;

    //newKey获取CompilationUnit中的包名路径
    private static List<String> newPkgUrl = null;


    //初始化数据和检查
    @Override
    public void initialize(ObfuscatorConfig config) {
        CollectionUtils.clear(fileMap, allResourcesMap);
        CollectionUtils.clear(newKey, newPkgUrl, targetPackages, oldPkgUrl, oldKey, sourcePackages);

        PackageRemoveParams packageRemoveParams = config.getPackageRemoveParams();
        sourceRoot = config.getSourceRoot();
        ExceptionUtils.checkNonNull(sourceRoot, "sourceRoot不能为空");

        delOldFiles = packageRemoveParams.isDelOldFiles();
        sourcePackages = packageRemoveParams.getSourcePackages();
        targetPackages = packageRemoveParams.getTargetPackages();

        ExceptionUtils.checkNonNull(sourcePackages, "请先选择源包！");
        ExceptionUtils.checkNonNull(targetPackages, "请先输入目标包名！");
        if (sourcePackages.size() != targetPackages.size()) throw new RuntimeException("转变前后的包数量需要一一对应");

        // 校验目标包是否已存在
        targetPackages.stream()
                .map(pkg -> new File(sourceRoot, pkg.replace(".", File.separator)))
                .filter(File::exists)
                .findFirst()
                .ifPresent(targetDir -> {
                    throw new RuntimeException(targetDir.getAbsolutePath() + "目标包已存在,暂不支持移动到已存在的包中");
                });

        if (sourcePackages.size() != targetPackages.size()) throw new RuntimeException("转变前后的包数量需要一一对应");

        isAndroidProject = packageRemoveParams.isAndroidProject();
        if (isAndroidProject) {
            resourceRoot = config.getResourceRoot();
            ExceptionUtils.checkNonNull(resourceRoot, "请设置ObfuscatorConfig中的资源resourceRoot路径");
            if (!FileUtil.exist(resourceRoot.trim()))
                throw new RuntimeException(resourceRoot + "路径不存在,请检查资源文件路径");
            if (!resourceRoot.trim().endsWith("/res"))
                throw new RuntimeException("安卓资源文件路径应该以/res文件夹结尾");
            if (config.getRunningStatus() != RunningEnum.ANDROID_PROjECT) {
                throw new RuntimeException("请设置ObfuscatorConfig中runningStatus字段为安卓项目");
            }
            allResourcesMap = new HashMap<>();

            //获取androidManifest.xml文件
            File resDir = new File(resourceRoot.trim());
            if (resDir.exists() && resDir.isDirectory()) {
                //获取父文件夹
                File parentDir = resDir.getParentFile();
                if (parentDir != null) {
                    File manifestFile = new File(parentDir, "AndroidManifest.xml");
                    if (manifestFile.exists() && manifestFile.isFile() && !allResourcesMap.containsKey(manifestFile.toString())) {
                        log.info("put AndroidManifest.xml into allResourcesMap");
                        allResourcesMap.put(manifestFile.toString(), manifestFile);
                    }
                }
            }
            //获取资源路径的所有xml文件
            FileUtils.loopXMLFiles(allResourcesMap, resourceRoot);
        }
        //获取BasePackage中所有的java文件
        FileUtils.loopFiles(fileMap, sourceRoot);
    }

    //混淆核心代码
    @Override
    public void obfuscate(ObfuscatorConfig config) {
        //1.筛选出fileMap中sourcePackages包下的所有K
        oldKey = ObfuscatorUtils.collectBelongsToPackageCLassFileKey(fileMap, sourcePackages);
        //获取oldKey中对应CompilationUnit的旧包名称
        oldPkgUrl = ObfuscatorUtils.getFilePackageNameByFileMapKey(fileMap, oldKey, true);

        //2.筛选出符合原路径包名下面的所有文件
        List<CompilationUnit> packageCLassFile = ObfuscatorUtils.collectBelongsToPackageCLassFile(fileMap, sourcePackages, null);
        ExceptionUtils.checkNonNull(packageCLassFile, "当前包下文件为空!");

        //3.包名重命名功能
        IntStream.range(0, targetPackages.size()).forEach(index -> {
            String s = sourcePackages.get(index);
            String t = targetPackages.get(index);
            log.info("ready to rename the current package:" + s + " to target package:" + t);
            packageCLassFile.forEach(item -> {
                //转换处理
                processPackage(s, t, item);
            });
        });

        //4.步骤3已经完成了包名重命名,所以这里重新筛选出fileMap中targetPackages包下的所有K,准备更改它的k为后面写出文件做准备
        newKey = ObfuscatorUtils.collectBelongsToPackageCLassFileKey(fileMap, targetPackages);
        //获取newKey对应的包名称
        newPkgUrl = ObfuscatorUtils.getFilePackageNameByFileMapKey(fileMap, newKey, true);

        //5.替换fileMap中所属包下的K为新的K(最后一个字段:是否删除旧文件)
        replaceFileMapKey(oldKey, oldPkgUrl, newKey, newPkgUrl, fileMap, delOldFiles);

        //6.更改引用地方:由于更改的是单个文件的包信息,需要将引用地方的包同样更改
        updateImports(fileMap, oldPkgUrl, newPkgUrl);

        //7.写出文件(最后一个字段:是否创建目标包路径)
        FileUtils.saveObfuscatedFiles(fileMap, true);

    }

    //是否启用这个功能
    @Override
    public boolean supports(ObfuscatorConfig config) {
        return ObjectUtil.isNotEmpty(config.getPackageRemoveParams());
    }

    // 包名重命名具体逻辑
    private static void processPackage(String sourcePackage, String targetPackage, CompilationUnit unit) {
        PackageDeclaration packageDeclaration = unit.getPackageDeclaration().get();
        //获取当前的包名
        String currentPackage = packageDeclaration.getNameAsString();

        //获取旧的类限定名
        String oldClassName = ObfuscatorUtils.getClassName(unit, true);

        // 如果当前包名等于源包名，直接替换为目标包名
        if (currentPackage.equals(sourcePackage)) {
            packageDeclaration.setName(targetPackage);
            if (isAndroidProject) {
                //更改xml引用
                updateXMLReference(oldClassName, ObfuscatorUtils.getClassName(unit, true));
            }
        } else if (currentPackage.startsWith(sourcePackage)) {
            // 当前包是源包下的子包，那么我就截取剩余的部分
            String lastSonPackage = currentPackage.substring(sourcePackage.length());
            //多做一次检查,万一遇到了多一个点
            if (lastSonPackage.startsWith(".")) {
                lastSonPackage = lastSonPackage.substring(1);  // 去除多余的点
            }
            // 拼接成新的目标包名
            String newTargetPackage = targetPackage + "." + lastSonPackage;
            packageDeclaration.setName(newTargetPackage);
            if (isAndroidProject) {
                //更改xml引用
                updateXMLReference(oldClassName, ObfuscatorUtils.getClassName(unit, true));
            }
        }
    }

    //替换掉fileMap中旧的k
    private static void replaceFileMapKey(List<String> oldKey, List<String> oldPkgUrl, List<String> newKey, List<String> newPkgUrl, Map<String, CompilationUnit> fileMap, boolean delOldFiles) {
        if (CollectionUtil.isEmpty(oldKey) || CollectionUtil.isEmpty(newKey))
            throw new RuntimeException("empty key list");

        if (CollectionUtil.isEmpty(oldPkgUrl) || CollectionUtil.isEmpty(newPkgUrl))
            throw new RuntimeException("empty pkg list");

        //size判断
        if ((oldKey.size() != newKey.size())
                || (oldPkgUrl.size() != newPkgUrl.size())
                || (oldKey.size() != oldPkgUrl.size())
                || (newKey.size() != newPkgUrl.size()))
            throw new RuntimeException("two list size are not equal");

        for (int i = 0; i < oldKey.size(); i++) {
            String ok = oldKey.get(i);
            String opu = oldPkgUrl.get(i);
            String npu = newPkgUrl.get(i);
            String nk = newKey.get(i);

            if (!ok.equals(nk)) {
                throw new RuntimeException("oldKey is not equal newKey");
            }
            String finalKey = nk.replace(opu, npu);
            CompilationUnit newCU = fileMap.get(nk);
            if (fileMap.containsKey(ok)) {
                //移除旧的key
                fileMap.remove(ok);
                //保存新的key
                fileMap.put(finalKey, newCU);
                if (delOldFiles) {
                    //删除文件
                    FileUtils.deleteJavaFilesInPackage(opu, sourceRoot);
                }
            }
        }
    }


    //包的引用修改
    private static void updateImports(Map<String, CompilationUnit> fileMap, List<String> oldPkgUrls, List<String> newPkgUrls) {
        //将原始包目录修改为import形式:com/pxt/living/utils -> com.pxt.living.utils
        final List<String> oldPkg = oldPkgUrls.stream().map(pkg -> pkg.replace(File.separator, ".")).collect(Collectors.toList());
        final List<String> newPkg = newPkgUrls.stream().map(pkg -> pkg.replace(File.separator, ".")).collect(Collectors.toList());

        if (CollectionUtil.isEmpty(oldPkg) || CollectionUtil.isEmpty(newPkg))
            throw new RuntimeException("pkg size can't be empty!");
        //遍历每个java文件
        for (Map.Entry<String, CompilationUnit> entry : fileMap.entrySet()) {
            CompilationUnit cu = entry.getValue();
            //获取每个java文件的导入包
            NodeList<ImportDeclaration> imports = cu.getImports();
            //如果当前java文件有导包,那我就把之前旧包的路径替换为新包路径
            if (imports.isNonEmpty()) {
                imports.forEach(pkg -> {
                    String pkgName = pkg.getNameAsString();
                    for (int i = 0; i < oldPkg.size(); i++) {
                        //原始旧的包名
                        String ok = oldPkg.get(i);
                        //新的目标包名
                        String np = newPkg.get(i);

                        //如果当前包名是以旧的包名开头,那么就替换
                        if (pkgName.startsWith(ok) || pkgName.contains(ok)) {
                            String targetPkg = pkgName.replace(ok, np);
                            pkg.setName(targetPkg);
                        }
                    }

                });
            }
        }
    }


    //xml资源文件引用的修改
    private static void updateXMLReference(String currentPackage, String targetPackage) {
        if (isAndroidProject && StrUtil.isNotEmpty(resourceRoot)) {
            ExceptionUtils.checkNonNull(allResourcesMap, "资源allResourcesMap为空,请检查resourceRoot是否填写正确");
            allResourcesMap.entrySet()
                    .stream()
                    .map(Map.Entry::getValue)
                    .forEach(file -> XmlUtils.replaceXmlValue(file, currentPackage, targetPackage));
        }
    }
}
